{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "通过列表生成式，我们可以直接创建一个列表。但是，受到内存限制，列表容量肯定是有限的。而且，创建一个包含100万个元素的列表，不仅占用很大的存储空间，如果我们仅仅需要访问前面几个元素，那后面绝大多数元素占用的空间都白白浪费了。 \n",
    "所以，如果列表元素可以按照某种算法推算出来，那我们是否可以在循环的过程中不断推算出后续的元素呢？这样就不必创建完整的list，从而节省大量的空间。在Python中，这种一边循环一边计算的机制，称为**生成器：generator**。 \n",
    "要创建一个generator，有很多种方法。第一种方法很简单，只要把一个列表生成式的`[]`改成`()`，就创建了一个**generator**."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]\n",
      "<generator object <genexpr> at 0x0000019E5723FB48>\n"
     ]
    }
   ],
   "source": [
    "L1 = [x * x for x in range(10)]\n",
    "print(L1)\n",
    "L2 = (x*x for x in range(10))\n",
    "print(L2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "创建L1和L2的区别仅在于最外层的[]和()，L1是一个list，而L2是一个generator。\n",
    "\n",
    "我们可以直接打印出list的每一个元素，但我们怎么打印出generator的每一个元素呢？ \n",
    "如果要一个一个打印出来，可以通过next()函数获得generator的下一个返回值："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "9\n",
      "16\n",
      "25\n"
     ]
    }
   ],
   "source": [
    "print(next(L2))\n",
    "print(next(L2))\n",
    "print(next(L2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们讲过，generator保存的是算法，每次调用`next(L2)`，就计算出`next(L2)`的下一个元素的值，直到计算到最后一个元素，没有更多的元素时，抛出`StopIteration`的错误。\n",
    "\n",
    "当然，上面这种不断调用`next(L2)`实在是太变态了，正确的方法是使用`for`循环，因为generator也是可迭代对象："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 1 4 9 16 25 36 49 64 81 "
     ]
    }
   ],
   "source": [
    "g = (x*x for x in range(10))\n",
    "for v in g:\n",
    "    print(v, end=\" \")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "所以，我们创建了一个generator后，基本上永远不会调用`next()`，而是通过for循环来迭代它，并且不需要关心`StopIteration`的错误。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "generator非常强大。如果推算的算法比较复杂，用类似列表生成式的`for`循环无法实现的时候，还可以用函数来实现。比如，著名的斐波拉契数列（Fibonacci），除第一个和第二个数外，任意一个数都可由前两个数相加得到：`1, 1, 2, 3, 5, 8, 13, 21, 34, ...`\n",
    "斐波拉契数列用列表生成式写不出来，但是，用函数把它打印出来却很容易："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "def fib(max):\n",
    "    n, a, b = 0, 0, 1\n",
    "    while n < max:\n",
    "        print(b)\n",
    "        a, b = b, a + b\n",
    "        n = n + 1\n",
    "    return 'done'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "仔细观察，可以看出，`fib`函数实际上是定义了斐波拉契数列的推算规则，可以从第一个元素开始，推算出后续任意的元素，这种逻辑其实非常类似generator。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "也就是说，上面的函数和generator仅一步之遥。要把`fib`函数变成generator，只需要把`print(b)`改为`yield b`就可以了："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "def fib(max):\n",
    "    n, a, b = 0, 0, 1\n",
    "    while n < max:\n",
    "        yield b\n",
    "        a, b = b, a + b\n",
    "        n = n + 1\n",
    "    return \"done\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这就是定义generator的另一种方法。如果一个函数定义中包含`yield`关键字，那么这个函数就不再是一个普通函数，而是一个generator："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<generator object fib at 0x0000019E5723F830>"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f = fib(6)\n",
    "f"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里，最难理解的就是generator和函数的执行流程不一样。函数是顺序执行，遇到`return`语句或者最后一行函数语句就返回。而变成generator的函数，在每次调用`next()`的时候执行，遇到`yield`语句返回，再次执行时从上次返回的yield语句处继续执行。\n",
    "举个简单的例子，定义一个generator，依次返回数字1，3，5："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "def odd():\n",
    "    print(\"step 1\")\n",
    "    yield 1\n",
    "    print(\"step 2\")\n",
    "    yield 3\n",
    "    print(\"step 3\")\n",
    "    yield 5"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "调用该generator时，首先要生成一个generator对象，然后用`next()`函数不断获得下一个返回值："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "step 1\n",
      "1\n",
      "step 2\n",
      "3\n",
      "step 3\n",
      "5\n"
     ]
    }
   ],
   "source": [
    "odd1 = odd()\n",
    "print(next(odd1))\n",
    "print(next(odd1))\n",
    "print(next(odd1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以看到，`odd`不是普通函数，而是generator，在执行过程中，遇到`yield`就中断，下次又继续执行。执行3次`yield`后，已经没有`yield`可以执行了."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "回到`fib`的例子，我们在循环过程中不断调用`yield`，就会不断中断。当然要给循环设置一个条件来退出循环，不然就会产生一个无限数列出来。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "同样的，把函数改成generator后，我们基本上从来不会用`next()`来获取下一个返回值，而是直接使用`for`循环来迭代："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 1 2 3 5 8 "
     ]
    }
   ],
   "source": [
    "for n in fib(6):\n",
    "    print(n, end=\" \")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "但是用`for`循环调用generator时，发现拿不到generator的`return`语句的返回值。如果想要拿到返回值，必须捕获`StopIteration`错误，返回值包含在`StopIteration`的`value`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "g  1\n",
      "g  1\n",
      "g  2\n",
      "g  3\n",
      "g  5\n",
      "g  8\n",
      "Generator return value:  done\n"
     ]
    }
   ],
   "source": [
    "g = fib(6)\n",
    "\n",
    "while True:\n",
    "    try:\n",
    "        x = next(g)\n",
    "        print(\"g \", x)\n",
    "    except StopIteration as e:\n",
    "        print(\"Generator return value: \", e.value)\n",
    "        break;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.5"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
